import type { App, ObjectDirective } from 'vue'
import { textFit, type TextFitOptions, type TextFitInstance } from '../textfit.js'

const KEY = Symbol('textfit') as unknown as '__textfit__'

type ElState = { instance: TextFitInstance, ro?: ResizeObserver, off?: () => void, scheduled?: boolean, rafId?: number, roTarget?: Element, lastW?: number, lastH?: number }
type ElWithInstance = HTMLElement & { [KEY]?: ElState }

function createDirective(): ObjectDirective<ElWithInstance, TextFitOptions | undefined> {
	return {
		mounted(el: ElWithInstance, binding: { value: TextFitOptions | undefined }) {
			const instance = textFit(el, binding.value || {})
			const state: ElState = { instance }

			const schedule = () => {
				if (state.scheduled) return
				state.scheduled = true
				state.rafId = requestAnimationFrame(() => {
					state.scheduled = false
					instance.resize()
				})
			}

			const roTarget = el.parentElement ?? el
			state.roTarget = roTarget
			const ro = new ResizeObserver((entries) => {
				for (const entry of entries) {
					if (entry.target !== roTarget) continue
					const { width, height } = entry.contentRect
					const dw = Math.abs(width - (state.lastW ?? width))
					const dh = Math.abs(height - (state.lastH ?? height))
					if (dw > 0.5 || dh > 0.5) {
						state.lastW = width
						state.lastH = height
						schedule()
					}
				}
			})
			ro.observe(roTarget)
			const onWin = () => schedule()
			window.addEventListener('resize', onWin, { passive: true })

			state.ro = ro
			state.off = () => window.removeEventListener('resize', onWin)
			el[KEY] = state
		},
		updated(el: ElWithInstance, binding: { value: TextFitOptions | undefined }) {
			if (!el[KEY]) return
			el[KEY]!.instance.setOptions(binding.value || {})
			if (!el[KEY]!.scheduled) {
				el[KEY]!.rafId = requestAnimationFrame(() => {
					el[KEY]!.scheduled = false
					el[KEY]!.instance.resize()
				})
				el[KEY]!.scheduled = true
			}
		},
		unmounted(el: ElWithInstance) {
			if (!el[KEY]) return
			el[KEY]!.ro?.disconnect()
			el[KEY]!.off?.()
			if (el[KEY]!.rafId) cancelAnimationFrame(el[KEY]!.rafId)
			el[KEY]!.instance.destroy()
			delete el[KEY]
		},
	}
}

export const textfitDirective = createDirective()

export function install(app: App) {
	app.directive('textfit', textfitDirective)
}

export default { install } 